package net.sk.china.front.service.impl.wechat;

import com.alibaba.fastjson.JSONObject;
import net.sk.china.common.constants.Constants;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.response.CreateOrderResponse;
import net.sk.china.common.utils.RestTemplateUtil;
import net.sk.china.common.utils.TalkUtil;
import net.sk.china.common.utils.WxPayUtil;
import net.sk.china.common.utils.XmlUtil;
import net.sk.china.common.vo.AttachVo;
import net.sk.china.common.vo.CreateOrderH5SceneInfoDetailVo;
import net.sk.china.common.vo.CreateOrderH5SceneInfoVo;
import net.sk.china.common.vo.CreateOrderVo;
import net.sk.china.front.request.WechatPayRequest;
import net.sk.china.front.service.system.SystemConfigService;
import net.sk.china.front.service.wechat.WeChatPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2018-2024 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2024/6/5 10:29
 * // +----------------------------------------------------------------------
 */
@Service
public class WeChatPayServiceImpl implements WeChatPayService {

    private final RestTemplateUtil restTemplateUtil;

    private final SystemConfigService systemConfigService;

    @Autowired
    public WeChatPayServiceImpl(RestTemplateUtil restTemplateUtil, SystemConfigService systemConfigService) {
        this.restTemplateUtil = restTemplateUtil;
        this.systemConfigService = systemConfigService;
    }

    /**
     * 统一支付
     * @param uid 用户ID
     * @param openId 微信openid
     * @param payType 支付方式 h5、routine、public、app
     * @param orderId 订单号
     * @param price 金额
     * @param clientIp 客户端IP
     * @param attach 支付附属数据
     * @return Map<String, String>
     */
    @Override
    public Map<String, String> wxPay(Integer uid, String openId, String payType, String orderId, BigDecimal price, String clientIp, String attach) {
        // 获取appId、mch_id、自定义的微信支付签名key
        String appId = "";
        String mchId = "";
        String signKey = "";

        // 公众号、H5
        if (payType.equals(Constants.PAY_CHANNEL_WE_CHAT_PUBLIC) || payType.equals(Constants.PAY_CHANNEL_WE_CHAT_H5)) {
            appId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WE_CHAT_APP_ID);
            mchId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WE_CHAT_MCH_ID);
            signKey = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WE_CHAT_APP_KEY);
        }

        // 小程序
        if (payType.equals(Constants.PAY_CHANNEL_WE_CHAT_PROGRAM)) {
            appId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_ROUTINE_APP_ID);
            mchId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_ROUTINE_MCH_ID);
            signKey = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_ROUTINE_APP_KEY);
        }

        // 微信APP支付
        if (payType.equals(Constants.PAY_CHANNEL_WE_CHAT_APP)) {
            appId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WX_APP_ID);
            mchId = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WX_APP_MCHID);
            signKey = systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_PAY_WX_APP_KEY);
        }

        // 获取微信预下单对象
        WechatPayRequest request = new WechatPayRequest();
        request.setUid(uid);
        request.setOrderId(orderId);
        request.setOpenId(openId);
        request.setPrice(price);
        request.setClientIp(clientIp);
        request.setAppId(appId);
        request.setMchId(mchId);
        request.setSignKey(signKey);
        request.setPayType(payType);
        request.setType(attach);

        // 生成微信统一下单对象
        CreateOrderVo vo = createOrderRequestVo(request);

        // 统一下单
        CreateOrderResponse response = unifiedOrder(vo);

        // 组装前端预下单参数
        Map<String, String> map = new HashMap<>(13);

        long currentTimestamp = WxPayUtil.getCurrentTimestamp();

        // 支付签名
        String paySign;
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_APP)) {
            // 微信APP支付 签名方式(paySign)：使用字段appid、timestamp、noncestr、prepayid计算得出的签名值,注意：加密的参数名全部都要转换成小写，不要然会提示签名失败
            map.put("appid", response.getAppId());
            map.put("prepayid", response.getPrepayId());
            map.put("partnerid", response.getMchId());
            map.put("package", "Sign=WXPay");
            map.put("noncestr", WxPayUtil.getNonceStr());
            map.put("timestamp", Long.toString(currentTimestamp));
            paySign = WxPayUtil.getSign(map, request.getSignKey());
            map.put("appId",map.get("appid"));
            map.put("nonceStr", map.get("noncestr"));
            map.put("timeStamp", map.get("timestamp"));
        } else {
            // JSAPI、小程序支付 签名方式(paySign)：使用字段appId、timeStamp、nonceStr、package计算得出签名值
            map.put("appId", response.getAppId());
            map.put("nonceStr", vo.getNonce_str());
            map.put("timeStamp", Long.toString(currentTimestamp));
            map.put("signType", vo.getSign_type());
            map.put("package", "prepay_id=".concat(response.getPrepayId()));
            paySign = WxPayUtil.getSign(map, request.getSignKey());
        }

        map.put("paySign", paySign);

        // H5支付
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_H5)) {
            map.put("web_url", response.getWebUrl());
        }
        map.remove("noncestr");
        map.remove("timestamp");
        map.remove("appid");
        return map;
    }

    /**
     * 生成微信统一下单对象
     * @param request 支付参数
     * @return {@code Map<String, String>}
     */
    private CreateOrderVo createOrderRequestVo(WechatPayRequest request) {
        // 获取域名
        String notifyUrl = systemConfigService.getValueByKeyException(Constants.CONFIG_WX_NOTIFY_URL);

        AttachVo attachVo = new AttachVo(request.getType(), request.getUid());
        CreateOrderVo vo = new CreateOrderVo();

        vo.setAppid(request.getAppId());
        vo.setMch_id(request.getMchId());
        vo.setNonce_str(WxPayUtil.getNonceStr());
        vo.setSign_type("MD5");
        vo.setBody("企融数科 - 支付中心");
        vo.setAttach(JSONObject.toJSONString(attachVo));
        vo.setOut_trade_no(request.getOrderId());
        // 订单中使用的是BigDecimal,这里要转为Integer类型
        vo.setTotal_fee(request.getPrice().multiply(BigDecimal.TEN).multiply(BigDecimal.TEN).intValue());
        vo.setSpbill_create_ip(request.getClientIp());
        vo.setNotify_url(notifyUrl);
        vo.setTrade_type("JSAPI");
        vo.setOpenid(request.getOpenId());


        // h5
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_H5)) {
            vo.setTrade_type("WEB");
            vo.setOpenid(null);
        }
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_APP)) {
            vo.setTrade_type("APP");
            vo.setOpenid(null);
        }
        CreateOrderH5SceneInfoVo createOrderH5SceneInfoVo = new CreateOrderH5SceneInfoVo(
                new CreateOrderH5SceneInfoDetailVo(
                        notifyUrl,
                        systemConfigService.getValueByKeyException(Constants.CONFIG_KEY_SITE_NAME)
                )
        );
        vo.setScene_info(JSONObject.toJSONString(createOrderH5SceneInfoVo));
        String sign = WxPayUtil.getSign(vo, request.getSignKey());
        vo.setSign(sign);

        return vo;
    }


    /**
     * 作用：统一下单
     * 场景：jsApi支付、小程序支付、扫码支付、APP支付、H5支付
     * @param vo 向微信发送的支付参数
     * @return {@code CreateOrderResponse}
     */
    private CreateOrderResponse unifiedOrder(CreateOrderVo vo) {
        try {
            String url = Constants.WX_PAY_API_URL + Constants.WX_PAY_API_URI;
            String request = XmlUtil.objectToXml(vo);
            String xml = restTemplateUtil.postXml(url, request);
            HashMap<String, Object> map = XmlUtil.xmlToMap(xml);
            if (null == map) {
                throw new TalkException("微信下单失败！");
            }
            CreateOrderResponse responseVo = TalkUtil.mapToObj(map, CreateOrderResponse.class);
            String fail = "FAIL";
            if (responseVo.getReturnCode().toUpperCase().equals(fail)) {
                throw new TalkException("微信下单失败1！" +  responseVo.getReturnMsg());
            }

            if (responseVo.getResultCode().toUpperCase().equals(fail)) {
                throw new TalkException("微信下单失败2！" + responseVo.getErrCodeDes());
            }

            responseVo.setExtra(vo.getScene_info());
            return responseVo;
        } catch (Exception e) {
            e.printStackTrace();
            throw new TalkException(e.getMessage());
        }
    }
}
